/*************************************************************************
	> File Name: 003.HZOJ224.cpp
	> Author: Maureen 
	> Mail: Maureen@qq.com 
	> Created Time: 一  2/22 13:25:06 2021
 ************************************************************************/

/**
 *题目描述
    已知一个数列，你需要进行下面三种操作：
        将某区间每一个数乘上 𝑥
        将某区间每一个数加上 𝑥
        求出某区间每一个数的和

输入
    第一行包含三个整数 𝑁,𝑀,𝑃，分别表示该数列数字的个数、操作的总个数和模数。
    第二行包含 𝑁 个用空格分隔的整数，其中第 𝑖 个数字表示数列第i项的初始值。

    接下来 𝑀 行每行包含 3 或 4 个整数，表示一个操作，具体如下：

        操作1 格式：1 𝑥 𝑦 𝑘 含义：将区间 [𝑥,𝑦] 内每个数乘上 𝑘
        操作2 格式：2 𝑥 𝑦 𝑘 含义：将区间 [𝑥,𝑦] 内每个数加上 𝑘
        操作3 格式：3 𝑥 𝑦 含义：输出区间 [𝑥,𝑦] 内每个数的和对 𝑃 取模所得的结果

输出
    输出包含若干行整数，即为所有操作 3 的结果。
 *
 */

#include <iostream>
#include <cstdio>
using namespace std;

#define MAX_N 100000

struct {
    long long sum;
    long long tag_t;
    long long tag_a;
} tree[MAX_N << 2];

long long arr[MAX_N  + 5];

long long n, m, p;

void UP(int ind) {
    tree[ind].sum = tree[ind << 1].sum + tree[ind << 1 | 1].sum;
    tree[ind].sum %= p;
    return ;
}

void buildSegmentTree(int ind, int l, int r) {
    tree[ind].tag_t = 1;
    tree[ind].tag_a = 0;
    if (l == r) {
        tree[ind].sum = arr[l];
        return ;
    }

    long long mid = (l + r) >> 1;
    buildSegmentTree(ind << 1, l, mid);
    buildSegmentTree(ind << 1 | 1, mid + 1, r);
    UP(ind);
    return ;
}

void times_tag(int ind, int val) {
    tree[ind].sum *= val;
    tree[ind].sum %= p;
    tree[ind].tag_t *= val;
    tree[ind].tag_t %= p;
    tree[ind].tag_a *= val;
    tree[ind].tag_a %= p;
    return ;
}

void add_tag(int ind, int val, int n) {
    tree[ind].sum += val * n;
    tree[ind].sum %= p;
    tree[ind].tag_a += val;
    tree[ind].tag_a %= p;
    return ;
}

void DOWN(int ind, int l, int r) {
    if (tree[ind].tag_t - 1 || tree[ind].tag_a) {
        long long t = tree[ind].tag_t;
        long long a = tree[ind].tag_a;
        long long mid = (l + r) >> 1;
        times_tag(ind << 1, t);
        times_tag(ind << 1 | 1, t);
        add_tag(ind << 1, a, mid - l + 1);
        add_tag(ind << 1 | 1, a, r - mid);
        tree[ind].tag_t = 1;
        tree[ind].tag_a = 0;
    }
    return ;
}

void modify(int flag, int ind, int l, int r, int x, int y, int val) {
    if (x <= l && r <= y) {
        if (flag) {
            times_tag(ind, val);
        } else {
            add_tag(ind, val, r - l + 1);
        }
        return ;
    }
    DOWN(ind, l, r);
    long long mid = (l + r) >> 1;
    if (x <= mid) {
        modify(flag, ind << 1, l, mid, x, y, val);
    }
    if (mid < y) {
        modify(flag, ind << 1 | 1, mid + 1, r, x, y, val);
    }
    UP(ind);
    return ;
}


long long query(int ind, int l, int r, int x, int y) {
    if (x <= l && r <= y) {
        return tree[ind].sum;
    }
    DOWN(ind, l, r);
    long long mid = (l + r) >> 1;
    long long ans = 0;
    if (x <= mid) {
        ans += query(ind << 1, l, mid, x, y);
        ans %= p;
    }
    if (mid < y) {
        ans += query(ind << 1 | 1, mid + 1, r, x, y);
        ans %= p;
    }
    return ans;
}

int main() {
    scanf("%lld%lld%lld", &n, &m, &p);
    for (int i = 1; i <= n; i++) scanf("%lld", arr + i);
    buildSegmentTree(1, 1, n);
    int a, x, y, k;
    for (int i = 0; i < m; i++) {
        scanf("%d", &a);
        switch (a) {
            case 1: scanf("%d%d%d", &x, &y, &k); modify(1, 1, 1, n, x, y, k); break;
            case 2: scanf("%d%d%d", &x, &y, &k); modify(0, 1, 1, n, x, y, k); break;
            case 3: scanf("%d%d", &x, &y); printf("%lld\n", query(1, 1, n, x, y)); break;
        }
    }

    return 0;
}
